all repos — caroster @ 8f7930deb0475683392d3660112a7b737f2b025a

[Octree] Group carpool to your event https://caroster.io

frontend/pages/e/[uuid]/details.tsx (view raw)

  1import moment from 'moment';
  2import Button from '@material-ui/core/Button';
  3import Box from '@material-ui/core/Box';
  4import Link from '@material-ui/core/Link';
  5import Paper from '@material-ui/core/Paper';
  6import Divider from '@material-ui/core/Divider';
  7import Container from '@material-ui/core/Container';
  8import TextField from '@material-ui/core/TextField';
  9import Typography from '@material-ui/core/Typography';
 10import {makeStyles} from '@material-ui/core/styles';
 11import {DatePicker} from '@material-ui/pickers';
 12import {PropsWithChildren, useState} from 'react';
 13import {useTranslation} from 'react-i18next';
 14import ShareEvent from '../../../containers/ShareEvent';
 15import useEventStore from '../../../stores/useEventStore';
 16import useToastStore from '../../../stores/useToastStore';
 17import useSettings from '../../../hooks/useSettings';
 18import EventLayout, {TabComponent} from '../../../layouts/Event';
 19import {initializeApollo} from '../../../lib/apolloClient';
 20import {
 21  EventByUuidDocument,
 22  useUpdateEventMutation,
 23} from '../../../generated/graphql';
 24
 25interface Props {
 26  eventUUID: string;
 27}
 28
 29const Page = (props: PropsWithChildren<Props>) => {
 30  return <EventLayout {...props} Tab={DetailsTab} />;
 31};
 32
 33const DetailsTab: TabComponent = ({}) => {
 34  const {t} = useTranslation();
 35  const settings = useSettings();
 36  const [updateEvent] = useUpdateEventMutation();
 37  const addToast = useToastStore(s => s.addToast);
 38  const setEventUpdate = useEventStore(s => s.setEventUpdate);
 39  const event = useEventStore(s => s.event);
 40  const [isEditing, setIsEditing] = useState(false);
 41  const idPrefix = isEditing ? 'EditEvent' : 'Event';
 42  const classes = useStyles();
 43
 44  const onSave = async e => {
 45    try {
 46      const {uuid, ...data} = event;
 47      const {id, travels, waitingPassengers, __typename, ...input} = data;
 48      await updateEvent({
 49        variables: {uuid, eventUpdate: input},
 50        refetchQueries: ['eventByUUID'],
 51      });
 52      setIsEditing(false);
 53    } catch (error) {
 54      console.error(error);
 55      addToast(t('event.errors.cant_update'));
 56    }
 57  };
 58
 59  const modifyButton = isEditing ? (
 60    <Button
 61      variant="contained"
 62      color="primary"
 63      className={classes.modify}
 64      onClick={onSave}
 65    >
 66      {t('event.details.save')}
 67    </Button>
 68  ) : (
 69    <Button
 70      variant="text"
 71      color="primary"
 72      className={classes.modify}
 73      onClick={() => setIsEditing(true)}
 74    >
 75      {t('event.details.modify')}
 76    </Button>
 77  );
 78
 79  if (!event) return null;
 80
 81  return (
 82    <Box className={classes.root}>
 83      <Container maxWidth="sm" className={classes.card}>
 84        <Paper className={classes.paper}>
 85          {modifyButton}
 86          <div className={classes.section}>
 87            <Typography variant="h6">{t('event.fields.name')}</Typography>
 88            {isEditing ? (
 89              <TextField
 90                fullWidth
 91                value={event.name}
 92                onChange={e => setEventUpdate({name: e.target.value})}
 93                name="name"
 94                id="EditEventName"
 95              />
 96            ) : (
 97              <Typography variant="body1" id={`${idPrefix}Name`}>
 98                {event.name ?? t('event.fields.empty')}
 99              </Typography>
100            )}
101          </div>
102          <div className={classes.section}>
103            <Typography variant="h6">{t('event.fields.date')}</Typography>
104            {isEditing ? (
105              <DatePicker
106                fullWidth
107                placeholder={t('event.fields.date_placeholder')}
108                value={event.date}
109                onChange={date =>
110                  setEventUpdate({
111                    date: !date ? null : moment(date).format('YYYY-MM-DD'),
112                  })
113                }
114                format="DD/MM/YYYY"
115                cancelLabel={t('generic.cancel')}
116                clearable
117                clearLabel={t('generic.clear')}
118                id={`${idPrefix}Date`}
119              />
120            ) : (
121              <Typography variant="body1" id={`${idPrefix}Date`}>
122                {event.date
123                  ? moment(event.date).format('DD/MM/YYYY')
124                  : t('event.fields.empty')}
125              </Typography>
126            )}
127          </div>
128          <div className={classes.section}>
129            <Typography variant="h6">{t('event.fields.address')}</Typography>
130            {isEditing ? (
131              <TextField
132                fullWidth
133                multiline
134                rowsMax={4}
135                inputProps={{maxLength: 250}}
136                helperText={`${event.address?.length ?? 0}/250`}
137                defaultValue={event.address}
138                value={event.address}
139                onChange={e => setEventUpdate({address: e.target.value})}
140                id={`${idPrefix}Address`}
141                name="address"
142              />
143            ) : (
144              <Typography variant="body1" id={`${idPrefix}Address`}>
145                {event.address ? (
146                  <Link
147                    target="_blank"
148                    rel="noreferrer"
149                    href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(
150                      event.address
151                    )}`}
152                    onClick={e => e.preventDefault}
153                  >
154                    {event.address}
155                  </Link>
156                ) : (
157                  t('event.fields.empty')
158                )}
159              </Typography>
160            )}
161          </div>
162          <div className={classes.section}>
163            <Typography variant="h6">
164              {t('event.fields.description')}
165            </Typography>
166            {isEditing ? (
167              <TextField
168                fullWidth
169                multiline
170                rowsMax={4}
171                inputProps={{maxLength: 250}}
172                helperText={`${event.description?.length || 0}/250`}
173                defaultValue={event.description}
174                value={event.description || ''}
175                onChange={e => setEventUpdate({description: e.target.value})}
176                id={`${idPrefix}Description`}
177                name="description"
178              />
179            ) : (
180              <Typography variant="body1" id={`${idPrefix}Description`}>
181                {event.description ?? t('event.fields.empty')}
182              </Typography>
183            )}
184          </div>
185          <Typography variant="h6">{t('event.fields.link')}</Typography>
186          <Typography>{t('event.fields.link_desc')}</Typography>
187          <Box py={4} justifyContent="center" display="flex">
188            <ShareEvent
189              title={`Caroster ${event.name}`}
190              url={`${window.location.href}`}
191            />{' '}
192          </Box>
193          <Divider variant="middle" />
194          <Box pt={2} justifyContent="center" display="flex">
195            <Link href={settings?.about_link} target="_blank" rel="noopener">
196              {t('event.details.aboutCaroster')}
197            </Link>
198          </Box>
199        </Paper>
200      </Container>
201    </Box>
202  );
203};
204
205const useStyles = makeStyles(theme => ({
206  root: {
207    position: 'relative',
208    paddingLeft: '80px',
209
210    [theme.breakpoints.down('sm')]: {
211      paddingLeft: 0,
212      paddingBottom: '80px',
213    },
214  },
215  paper: {
216    position: 'relative',
217    padding: theme.spacing(2),
218  },
219  card: {
220    marginTop: theme.spacing(6),
221  },
222  modify: {
223    position: 'absolute',
224    right: theme.spacing(2),
225  },
226  section: {
227    marginBottom: theme.spacing(2),
228    width: '540px',
229    maxWidth: '100%',
230    paddingX: theme.spacing(2),
231  },
232  map: {
233    marginTop: theme.spacing(4),
234  },
235  seeOnGMapButton: {
236    marginLeft: theme.spacing(2),
237  },
238}));
239
240export async function getServerSideProps(ctx) {
241  const {uuid} = ctx.query;
242  const apolloClient = initializeApollo();
243  const {data = {}} = await apolloClient.query({
244    query: EventByUuidDocument,
245    variables: {uuid},
246  });
247  const {eventByUUID: event} = data;
248  const {host = ''} = ctx.req.headers;
249
250  return {
251    props: {
252      event,
253      eventUUID: uuid,
254      metas: {
255        title: event?.name || '',
256        url: `https://${host}${ctx.resolvedUrl}`,
257      },
258    },
259  };
260}
261
262export default Page;